\chapter{Narcissus}
\label{Narcissus}

Voor het prototype zal er gebruik gemaakt worden van Narcissus als client-side interpreter zoals beschreven in hoofdstuk \ref{Architectuur}.

Narcissus werd eerst ontwikkeld door Brendan Eich, de uitvinder van JavaScript, als proof-of-concept.
Daarna werd Narcissus verder ontwikkeld om het te gebruiken als engine waarin nieuwe taalfeatures voor JavaScript zouden kunnen getest worden. 
Narcissus is hier goed voor geschikt omdat het een veel kleinere codebase heeft dan een doorsnee JavaScript engine. 
Daarbij komt nog dat het geschreven is in JavaScript, een dynamische scripttaal waarin snel ontwikkeld kan worden.
Momenteel werkt Narcissus enkel als het draait binnen SpiderMonkey\cite{spidermonkey}, de JavaScript engine van Firefox. Narcissus is echter wel enkel een research project
en geen volwaardige JavaScript interpreter zoals degene aanwezig in web browsers.
Het is dus nodig om te testen hoe goed Narcissus JavaScript ondersteunt.

Dit hoofdstuk beschrijft de werking van Narcissus, het testproces en de testresultaten.


\section{Werking}

Alhoewel het concept van een JavaScript engine die geschreven is in JavaScript vrij verwarrend 
kan zijn, is de manier hoe Narcissus in elkaar zit helemaal niet zo bijzonder. Het is makkelijker in te 
beelden dat het een andere programmeertaal evalueert, het blijft qua concept hetzelfde.

Narcissus werkt zelf als een doorsnee interpreter, zo wordt er gebruik gemaakt van een lexer en 
een parser om de programma tekst om te zetten in een interne gegevensstructuur die een abstract 
syntax tree (ast) of parse tree genoemd wordt. Deze gegevensstructuur wordt dan diepte eerst uitgevoerd en zo wordt de code ge\"evalueerd. 
Veel van de standaard operaties zijn in Narcissus niet helemaal opnieuw ge\"implementeerd en maken gebruik (als een soort facade) van de versie die de JavaScript engine van de browser gebruikt.
Dit zijn de heel elementaire operaties zoals de wiskundige bewerkingen (\lstinline|+|, \lstinline|-|, \lstinline|*|, \lstinline|/|, etc), booleaanse operaties (\lstinline|&&|, \lstinline|==|, \lstinline|!=|, etc) en andere functies (\lstinline|document.alert()|, \lstinline|document.cookie|, etc). 

\begin{figure}
		\centering
 		\includegraphics[width=0.80\textwidth]{img/werking_narcissus.png}
 		\caption{Diagram: werking van Narcissus}
		\label{figWerkingnarc}
\label{Diagram: werking van Narcissus}
\end{figure}

\pagebreak

Figuur \ref{figWerkingnarc} toont een schematische voorstelling van hoe een stuk code ge\"evalueerd wordt. 
\begin{itemize}
  \setlength{\parskip}{0.0in}
  \setlength{\itemindent}{1.0em}
	\item In stap 1 zal SpiderMonkey alle bestanden van Narcissus uitvoeren. (jsparse, jsexec,..)
	\item In stap 2 zal Narcissus een stukje code beginnen evalueren. De codestring wordt geparset en omgezet naar tokens.
	\item In stap 3 zal de code een oproep doen naar een built-in JavaScript functie. Narcissus bepaald aan de hand van de token welke functie er uitgevoerd moet worden voor die bepaalde token(s).
	\item In stap 4 zal Narcissus deze oproep doorgeven aan SpiderMonkey.
\end{itemize}

\subsection{Code uitvoering in Narcissus}

Het inladen van Narcissus is zeer eenvoudig. De bestanden waaruit Narcissus bestaat, kunnen via de \lstinline|script|-tag aan de pagina worden toegevoegd.

\medskip
\begin{lstlisting}[caption=Inladen van Narcissus.]
  <script type="text/javascript;version=1.7" src="lib/jsdefs.js"> </script>
  <script type="test/javascript;version=1.7" src="lib/jslex.js" > </script>
  ...
  <script type="text/javascript;version=1.7" src="lib/jsexec.js" > </script>
\end{lstlisting}

Scripts uitvoeren in Narcissus is het best te vergelijken met de \lstinline|eval| functie van javascript. 
Deze neemt een string aan als argument, die ge\"interpreteerd wordt als code en meteen uitgevoerd wordt.

\medskip
\begin{lstlisting}[caption=Inladen van Narcissus.]
  <script type="text/javascript;version=1.7">
    Nacissus.interpreter.evaluate('alert("Hello, world!");');
  </script>
\end{lstlisting}

Narcissus heeft zelf nog geen methode om de code vanuit bestanden in te lezen, hiervoor is een methode geschreven die dit wel mogelijk maakt. 
Dit is niet enkel belangrijk voor de tests, ook in het prototype zal er uit bestanden moeten kunnen worden ge\"evalueerd.
Deze methode maakt gebruik van het \lstinline|XMLHttpRequest| object. Dit brengt wel een limitatie met zich mee. 
Aangezien de \lstinline|XMLHttpRequest| enkel request binnen zijn origin kan sturen, kunnen er enkel lokale JavaScript bestanden ge\"evalueerd worden.

\medskip
\begin{lstlisting}[caption=Externe bestanden uitvoeren.]
  <script type="application/javascript;version=1.7">
    Nacissus.interpreter.evaluateUrl('http://example.com/myJSfile.js');
  </script>
\end{lstlisting}

\medskip
\begin{lstlisting}[caption=Implementatie van Narcissus.evaluateUrl() (in lib/jsexec.js).]
  function evaluateUrl(u, p, f, l){
     eval_req.open('GET', u, false);
     eval_req.send(null);
     if (eval_req.status !== 200) {
        throw new Error("Error loading " + u);
     }
     return evaluate(eval_req.responseText,p,f,l);
  }
\end{lstlisting}

\pagebreak

\section{Testproces}
Omdat Narcissus slechts een research prototype is, was het nodig om de geschiktheid van Narcissus voor deze thesis grondig te onderzoeken.
Hiervoor hebben we een testsuite ontwikkeld die Narcissus test op het correct uitvoeren van JavaScript.

\subsection{De Testsuite}
Een goede beschrijving van wat JavaScript moet doen, is terug te vinden in de ECMAScript specificaties \cite{ecma}.
Hiervoor zijn ook al reeds tests geschreven, die deze functionaliteiten testen \cite{ecmatest}. Deze testsuite bevat meer dan 10000 tests en kan gewoon in de browser uitgevoerd worden. Ook SpiderMonkey gebruikt deze tests. 
Aangezien de tests in Narcissus moeten uitgevoerd worden, moest er een alternatieve methode gezocht worden om deze tests toch te kunnen uitvoeren.

De tests die gebruikt worden zijn ook te downloaden via die site, in totaal dus iets meer dan 10000 bestanden die allen een stukje JavaScript code bevatten dat een bepaalde functionaliteit test. 
De bestanden zijn verspreid over verschillende mappen per hoofdstuk. De hoofdstukken zijn dezelfde als in de ECMAScript specificaties \cite{ecma}.
De tests behandelen wel enkel hoofdstukken 7 tot en met 15. Dit is omdat de eerste 6 hoofdstukken van de specificaties geen echte functionaliteit beschrijven.
Dit kan later interessant zijn om te kunnen achterhalen welke delen er in Narcissus nog onvoldoende ondersteund worden.

Sommige functionaliteiten zoals onder andere DOM, XHR, en cookie, vallen niet binnen de ECMA specificaties.
Voor deze functionaliteiten zullen er aanvullende tests geschreven moeten worden. 

\subsubsection{Aanpassingen aan de Testsuite}
\label{AanpassenTestSuite}

Het is onwenselijk dat 2 tests elkaar be\"invloeden, om dit te vermijden is het dus best om geen 2 tests achter elkaar in hetzelfde venster uit te voeren. 
In de online testsuite wordt er telkens een nieuwe frame aangemaakt, daarin wordt de test met het script geladen en zo de resultaten opgevraagd. 
Dit bleek niet zo makkelijk te dupliceren te zijn, zeker om Narcissus telkens opnieuw mee in te laden en de test correct in Narcissus te laten uitvoeren.
De methodes die nagaan of het correcte resultaat is verkregen zijn mits een lichte aanpassing wel gekopi\"eerd (e.g. De methode die aangeeft dat een test een ander resultaat dan verwacht verkregen heeft). 
Deze functies moeten mee ingeladen worden in Narcissus voor de eigenlijke test ingeladen wordt, omdat de methodes in de tests ook in Narcissus uitgevoerd worden.
Ook deze methodes zijn op voorhand getest, zodat er geen tests uit de testsuite zouden falen door het falen van deze hulpmethodes.

\pagebreak

Zo kan er al getest worden voor elke test of deze slaagt. Uiteraard is het onbegonnen werk om ze allemaal een per een af te gaan en de resultaten te noteren.
Daarvoor is er een script gemaakt dat zichzelf na het uitvoeren van een test kan doorsturen naar de volgende tot er geen test meer overblijft. 
Opnieuw niet zo magisch, het aanpassen van \lstinline|document.location| laadt de ingegeven url in de frame waar het script uitgevoerd wordt. 
D.w.z. dat de gehele frame opnieuw wordt geladen, ook de bestanden van Narcissus.

Voor te bepalen welke test er geladen moet worden en welke test de volgende moet zijn is er nood aan een dynamische webtaal. 
Aangezien PHP eenvoudig te gebruiken is, wordt daarvoor geopteerd, in samenhang met een MySQL database. 
Een eerste script zorgt dat elke test in de database terecht komt en een uniek volgnummer krijgt, dit moet slechts eenmaal gebeuren.
Vanaf dan kan er eenvoudig naar de volgende test doorverwezen worden. Door te beginnen met nummer 1 en elke keer te verhogen tot de nummer groter is dan het aantal tests in de database.

\medskip
\begin{lstlisting}
  $testfile = $test_model->getTest($_GET["scriptnr"]);
  loadTest("<?=$testfile?>");
  ...
  window.location = "chain.php?scriptnr="+(scriptNR+1):
\end{lstlisting}

Voor het bijhouden van de resultaten kan dan dezelfde database gebruikt worden. Na het uitvoeren er van, kan de eventuele fout weggeschreven worden voor die bijhorende test. 
Dit kan met XHR, waarvoor een apart PHP script zorgt voor de afhandeling van de database operaties. 
Er mag niet naar de volgende test gegaan worden alvorens er melding is gedaan van de eventuele fout.
Als dit niet correct gedaan wordt en de XHR request is niet tijdig verzonden, zal de request ook niet verder afgehandeld worden, waardoor de mogelijkheid bestaat dat falende tests niet worden opgemerkt.
Om hier zeker te kunnen van zijn kan er gebruik gemaakt worden van de callback parameter van XHR, die opgeroepen wordt na het verkrijgen van de response van de server.
Door de methode die het doorverwijzen naar de volgende test afhandelt als callback te nemen, kan er geen foutmelding overgeslagen worden.

\subsection{Tests}

Aangezien de tests ook voor Spidermonkey gebruikt worden, is het interessant om de vergelijking te maken met deze JavaScript Engine die zich al bewezen heeft op het wereldwijde web.
Mede ook omdat Narcissus uitgevoerd wordt in Spidermonkey, waardoor falende tests aan de basis kunnen liggen van falende tests in Narcissus.

Van de 10405 geteste files faalden er slechts 174 in SpiderMonkey. Narcissus doet het minder goed, er falen in totaal 2446 files (zie Figuur \ref{figTestRes1}). 
Gefaalde tests zijn in de eerste plaats de tests die niet slagen omdat de \$ERROR of \$FAIL methode wordt opgeroepen in de test. Deze 2 methodes zitten in de test om aan te geven of een test al dan niet faalt. 
Maar ook de tests waarbij een error gegooid wordt in SpiderMonkey, hierover later meer.

\begin{figure}
		\centering
 		\includegraphics[width=0.80\textwidth]{img/test_chart.png}
 		\caption{Testresultaten.}
		\label{figTestRes1}
\label{Testresultaten.}
\end{figure}

\subsection{Patches}

Nu de resultaten ter beschikking zijn, moet er gekeken worden wat de oorzaak is van deze falende tests. 
In de hoop dat enkele kleine aanpassingen, veel problemen oplossen.
Deze analyse zal een bijdrage zijn aan de volledige thesis. 
De patches die gedaan zullen worden kunnen gemeld worden aan Mozilla ter ondersteuning van de ontwikkeling van Narcissus. 
Het is niet de bedoeling om Narcissus volledig foutloos te krijgen, een minimum aan functionaliteit ondersteunen volstaat voor het prototype.

\subsubsection{Patch 1: Undefined variables}

Een eerste probleem dat onderzocht is geweest, is het gebruik van ongedefinieerde variabelen en functies. 
Indien men een variabele "\lstinline|x|" wilt gebruiken zonder dat deze eerst ergens is gedeclareerd, wordt er verwacht dat JavaScript een \lstinline|ReferenceError| gooit. 

\medskip
\begin{lstlisting}[caption=Falende test.]
  try{
      eval("onbekende_var;");
      $ERROR("2.1.3 ACCESS UNDEFINED VARIABLES > ERROR EXPECTED");
   }catch(e){
      success("2.1.3 ACCESS UNDEFINED VARIABLES / ERROR EXCPETED: "+e.message);
   }
\end{lstlisting}

\pagebreak

Na analyze van Narcissus blijkt dat Narcissus wel degelijk de verwachte fout gooit maar in de browser context. 
Omdat de tests draaien in de interpreter context kunnen zij deze fout niet vangen. 
Dit probleem kan opgelost worden door de fout door Narcissus te laten evalueren.

\medskip
\begin{lstlisting}[caption=Oplossing.]
  //oude Narcissus code
  function getValue(v) {
     ...
     throw new ReferenceError(v.propertyName + ' is not defined, ' + v.node.filename + ', ' + v.node.lineno);
     ...
  }
  
  //aangepaste code
  function getValue(v) {
     ...
     var e = 'throw new ReferenceError("'+ v.propertyName + ' is not defined, ' + v.node.filename + ', ' + v.node.lineno + '");';
     Narcissus.interpreter.evaluate(e);
     ...
  }
\end{lstlisting}

Het blijkt dat een groot deel van de tests faalt om gelijkaardige reden, fouten die vanwege de context niet kunnen opgevangen worden (Zie Figuur \ref{figTestResPie}). 
Voor een deel van de gevallen is dit opgelost, zonder hierbij nieuwe tests te doen falen. 

\subsubsection{Patch 2: intanceof}

Een andere reden waardoor verschillende tests faalde, was het gebruik van \lstinline|instanceof|. 
Buiten het testen van de \lstinline|instanceof| functionaliteit waren er nog een hoop tests die afhingen van het correct werken ervan.
In het vorige voorbeeld wordt er bijvoorbeeld nagegaan dat de opgevangen fout een instantie is van \lstinline|ReferenceError|. 
In Narcissus werd een eigen implementatie gebruikt van \lstinline|instanceof|. Deze werkte niet correct en is vervangen door de \lstinline|instanceof| van SpiderMonkey.

\subsubsection{Patch 3: XMLHttpRequest}

Er was ook een probleem met de constructor van het \lstinline|XMLHttpRequest| (XHR) object, bij het oproepen van deze contructor zou er een 
\lstinline|TypeError| gesmeten worden. Dit kwam omdat Narcissus een bepaalde functie, \lstinline|__construct__()|,  van XHR niet kon vinden. 
Dit was een functie die Narcissus zelf aan het prototype van het \lstinline|Function| object zou moeten toegevoegd hebben die het gebruikt bij het evalueren van constructoren.

\pagebreak

\medskip
\begin{lstlisting}[caption=Narcissus definieert de \_\_construct\_\_ functie op het prototype van het Function object.]
 definitions.defineProperty(Function.prototype, "__construct__",
        function (a, x) {
           a = Array.prototype.splice.call(a, 0, a.length);
           switch (a.length) {
              case 0:
                 return new this();
              case 1:
                 return new this(a[0]);
              case 2:
                 return new this(a[0], a[1]);
              case 3:
                 return new this(a[0], a[1], a[2]);
              default:
                 var argStr = "";
                 for (var i=0; i<a.length; i++) {
                    argStr += 'a[' + i + '],';
                 }
                 return eval('new this(' + argStr.slice(0,-1) + ');');
           }
        }, true, true, true);
\end{lstlisting}


De reden dat deze functie niet gedefinieerd is voor (XHR), is dat het een speciaal prototype heeft. Want je zou nochthans verwachten dat XHR, dat zelf ook een functie is, alle functionaliteiten van \lstinline|Function| overerft.
XHR wordt namelijk beschikbaar gesteld aan JavaScript door Firefox via het XPCON systeem (een systeem dat c++ objecten beschikbaar kan stellen aan JavaScript code) en daardoor is het prototype
van XHR een zogenaamd "XPCON wrapped native object". Dit prototype is zo beveiligd dat zijn eigenschappen niet aangepast kunnen worden.
Het was daardoor nodig om de \lstinline|__construct__()| functie rechtstreeks aan XHR toe te voegen.


\medskip
\begin{lstlisting}[caption=De \_\_construct\_\_() functie wordt rechtreeks gedefinieerd op  XHR]
 definitions.defineProperty(XMLHttpRequest, "__construct__",
        function (a, x) {
           a = Array.prototype.splice.call(a, 0, a.length);
           switch (a.length) {
              case 0:
                 return new this();
              case 1:
                 return new this(a[0]);
              case 2:
                 return new this(a[0], a[1]);
              case 3:
                 return new this(a[0], a[1], a[2]);
              default:
                 var argStr = "";
                 for (var i=0; i<a.length; i++) {
                    argStr += 'a[' + i + '],';
                 }
                 return eval('new this(' + argStr.slice(0,-1) + ');');
           }
        }, true, true, true);
\end{lstlisting}


\subsubsection{Kleinere patches}

Daarbuiten zijn er ook kleinere patches gedaan. Het inlezen van getallen die beginnen met een 
punt of getallen die beginnen met een 0 gevolgd door een punt. Deze problemen situeerden zich in 
de lexer, bij het omzetten van de string werd teruggegeven het hoeveelste token het was in plaats 
van de waarde ervan. Op zich waren dit geen grote aanpassingen, maar het geeft wel een zicht op 
de werking van de lexer. Uiteraard lossen deze patches slechts enkele testgevallen op.


\subsection{Resultaten}

\begin{figure}
		\centering
 		\includegraphics[width=0.80\textwidth]{img/test_chart2.png}
 		\caption{Testresultaten na het patchen.}
		\label{figTestRes2}
\label{Testresultaten na het patchen.}
\end{figure}

Voor de nieuwe Narcissus slagen 292 test meer dan de versie waarmee er de eerste keer getest is geweest (zie Figuur \ref{figTestRes2}).
De falende tests zijn verspreid over de veschillende hoofdstukken (zie Figuur \ref{figTestResChap}). 


De hoofdstukken beschrijven:
\begin{itemize}
  \setlength{\parskip}{0.0in}
  \setlength{\itemindent}{1.0em}
  \item Chapter 7: Lexical Conventions
  \item Chapter 8: Types
  \item Chapter 9: Type Conventions
  \item Chapter 10: Executable Code and Execution Contexts
  \item Chapter 11: Expressions
  \item Chapter 12: Statements
  \item Chapter 13: Function Definition
  \item Chapter 14: Program
  \item Chapter 15: Standard Built-in ECMAScript Objects
\end{itemize}

Er is dus geen een specifiek hoofdstuk dat slecht of niet ondersteund wordt, de grootste delen van elk hoofdstuk zijn ondersteund.
Van de overblijvende fouten falen er nog steeds 1041 omdat er een fout in de browser context gesmeten wordt dat niet in
de interpreter context opgevangen kan worden. Verder zijn er 286 van de fouten de wijten aan het feit dat Narcissus \lstinline|"use strict"| niet ondersteunt (Zie Figuur \ref{figTestResPie}).

\begin{figure}	
		\centering
 		\includegraphics[width=0.80\textwidth]{img/test_chapters_chart.png}
 		\caption{Testresultaten per hoofdstuk.}
		\label{figTestResChap}
\label{Testresultaten per hoofdstuk.}
\end{figure}

\begin{figure}
		\centering
 		\includegraphics[width=0.80\textwidth]{img/test_pie_chart.png}
 		\caption{Verhouding van tests die falen door uncaught Exceptions en use strict.}
		\label{figTestResPie}
\end{figure}

\pagebreak

\section{Evaluatie}

Hierbij wordt het testprocess rond Narcissus afgerond. Van de overblijvende tests die falen door de exceptions die gesmeten worden in de browser context, 
en dus niet opgevangen kunnen worden in de interpreter context, zijn er waarschijnlijk een deel die nog kunnen opgelost worden op dezelfde manier dan deze eerder beschreven.
Het is wel zeker dat er gevallen zijn die een fout smijten in de browser context door een andere reden.

De tests die hier als 1 test beschouwd worden, zijn eigenlijk testbestanden die bestaan uit meerdere tests. Elke test controleert dan een gelijkaardige functionaliteit.
Het gebeurt dan wel dat juist een van de laatste tests uit een bestand faalt. 
Meestal is dit ook een iets $"$exotischer$"$ geval, waarbij meer uitzonderlijke functionaliteit getest wordt. 
Dit kan een vertekend beeld geven, omdat hetzelfde percentage van ondersteunde functionaliteit hoger ligt dan uit de tests blijkt.

Narcissus staat zeker niet op punt maar het wordt nog steeds verder ontwikkeld door Mozilla. 
Narcissus ondersteunt de basis functionaliteiten van JavaScript zo goed als volledig, 
het is vooral te wijten aan de meest exotischere features van JavaScript dat er zo veel tests falen.
Daardoor hebben we besloten dat Narcissus geschikt is om verder gebruikt te worden in het prototype.
